/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse.alert;

import java.net.*;
import javax.net.*;
import java.sql.*;
import java.util.Properties;

import nfse.*;

/*******************************************************************************
 * Like the QueryServer, DataServer and StatsServer, this process starts threads
 * and waits for cients to connect. In this case, alert information is sent from
 * a client (currently a nfse.DataSocket). When the client connects, a
 * AlertSocket thread is created and started.
 * 
 * @author bduphoff
 * 
 */
public class AlertServer extends Thread {

    /***************************************************************************
     * The port number that will be used to listen for TCP connections
     */
    private int port = -1;

    /***************************************************************************
     * A JDBC connection to the alert database. The connection is setup the
     * first time that createStatement() is called.
     */
    public Connection connection = null;

    /***************************************************************************
     * Basic constructor specifies what port to listen on.
     * 
     * @param portNum
     *            The port number on which to listen for connections
     */
    public AlertServer(int portNum) {
        this.port = portNum;
    }

    /***************************************************************************
     * Much like the DataServer and QueryServer. Continuously creates a new
     * AlertSocket which blocks until a client connects.
     */
    public void run() {
        try {
            System.out.println("Alert Server: Listening on port " + port + ".");
            ServerSocketFactory ssf = null;
            ssf = ServerSocketFactory.getDefault();
            ServerSocket ss = ssf.createServerSocket(port);
            // address = ss.getInetAddress();

            while (true) {
                AlertSocket sock = new AlertSocket(ss.accept(), this);
                sock.start();
                System.out.println("Alert Server: Opened new alert socket.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***************************************************************************
     * Adds records to the alert database. Future versions could make this a
     * buffered process like the StatsServer, although the real-time property of
     * this implementation is desirable.
     * 
     * @param statement
     *            The JDBC statement connected to the alerts database
     * @param detectionTS
     *            The time in milliseconds at which the AlertSocket processed
     *            the alert
     * @param rec
     *            The alert record to be added to the normalized alert database
     * @throws Exception
     *             Thrown when the database statement/connection fails or the
     *             record is null
     */
    public void addRecord(Statement statement, long detectionTS, NetFSERecord rec) throws Exception {
        if (!rec.isAlert())
            return;

        Timestamp detectTS = new Timestamp(detectionTS);
        Timestamp endTS = rec.getEndTS();
        Timestamp startTS = rec.getStartTS();
        String dstIP = rec.getDstAddr();
        String srcIP = rec.getSrcAddr();
        int dstPort = rec.getDstPort();
        int srcPort = rec.getSrcPort();
        int protocol = rec.getProtocol();
        String message = rec.getAlertMessage();

        ResultSet rs = statement.executeQuery("select nextval('alert_seq')");
        rs.next();
        int id = rs.getInt(1) % 100000;
        String sql = "insert into alerts values (" + id + "," + rec.getSensorID() + "," + rec.getTypeID() + ",'" + detectTS + "','"
                + startTS + "','" + endTS + "','" + srcIP + "','" + dstIP + "'," + srcPort + "," + dstPort + ","
                + protocol + ",'" + message.replaceAll("\'", " ") + "','" + NetFSEUtil.convertToHex(rec.getInitialString())
                + "')";

        try {
            statement.execute(sql);
        } catch (SQLException e) {
            sql = "update alerts set sensorid=" + rec.getSensorID() + ", typeid=" + rec.getTypeID() + ",detectts='" + detectTS
                    + "',startts='" + startTS + "',endts='" + endTS + "',srcip='" + srcIP + "',dstip='" + dstIP
                    + "',srcport=" + srcPort + ",dstport=" + dstPort + ",protocol=" + protocol + ",message='"
                    + message.replaceAll("\'", " ") + ",recordhex='" + NetFSEUtil.convertToHex(rec.getInitialString())
                    + "' where id=" + id;
            statement.execute(sql);
        }

    }

    /***************************************************************************
     * Opens a JDBC connection to the alerts database (if needed) and creates a
     * statement allowing access to the alerts database.
     * 
     * @return A Statement connected to the alerts database
     * @throws Exception
     *             Thrown if the database server is down or the authentication
     *             parameters are invalid.
     */
    protected Statement createStatement() throws Exception {
        Statement s = null;
        try {
            s = connection.createStatement();
        } catch (Exception e) {
            Class.forName("org.postgresql.Driver");
            String url = "jdbc:postgresql://" + NetFSE.ALERT_DB_HOST + "/" + NetFSE.ALERT_DB_DATABASE;
            Properties props = new Properties();
            props.setProperty("user", NetFSE.ALERT_DB_USER);
            props.setProperty("password", NetFSE.ALERT_DB_PASSWORD);
            props.setProperty("client_encoding", "UTF8");

            connection = DriverManager.getConnection(url, props);

            s = connection.createStatement();
        }
        return s;
    }

}